package com.study.springbootsecurity.config;

import com.study.springbootsecurity.config.properties.JwtConfigProperties;
import com.study.springbootsecurity.filter.JwtTokenOncePerRequestFilter;
import com.study.springbootsecurity.filter.LoginTokenFilter;
import com.study.springbootsecurity.handler.pass.LogoutTokenSuccessHandler;
import com.study.springbootsecurity.service.AccessTokenBlackService;
import com.study.springbootsecurity.service.impl.TokenService;
import com.study.springbootsecurity.service.impl.UserDetailsServiceImpl;
import com.study.springbootsecurity.util.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Description: TODO spring security6 安全配置类
 * @author: Huangjianyun
 * @date: 2025-06-05 19:04
 */
@Configuration
@EnableWebSecurity()
@EnableMethodSecurity(securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig {


    @Autowired
    UserDetailsServiceImpl userDetailsServiceImpl;

    @Autowired
    private JwtConfigProperties jwtConfigProperties ;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    AuthenticationConfiguration authenticationConfiguration;

    @Autowired
    TokenService tokenService;

    @Autowired
    AccessTokenBlackService blackService;

//    @Autowired
//    SmsCodeAuthenticationSecurityConfig smsCodeSecurityConfig;
// 这条过滤器链 匹配密码登录请求，放行登录接口和swagger静态资源 其他接口都要进行认证，添加密码过滤器 jwt令牌过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain baseAuthSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // 禁用框架的 X-Frame-Options 响应头，以允许页面在 iframe 中显示
        httpSecurity.headers(headersConfigurer->headersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));
        // 局部配置
        httpSecurity.securityMatcher(/* PathPatternRequestMatcher.withDefaults()
                        .matcher(HttpMethod.POST,"/api/user/login")*/"/api/user/login")
                    .authorizeHttpRequests(authorize -> {
                            authorize
                                    .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                                    .requestMatchers(HttpMethod.POST, "/api/user/login").permitAll()
                                    .anyRequest().authenticated();})
                    .authenticationProvider(authenticationProvider())
                    .authenticationManager(authenticationManager(authenticationConfiguration))
                    .addFilterAt(new LoginTokenFilter(authenticationManager(authenticationConfiguration),redisCache,jwtConfigProperties,tokenService), UsernamePasswordAuthenticationFilter.class)
                    .addFilterBefore(new JwtTokenOncePerRequestFilter(userDetailsServiceImpl,jwtConfigProperties,tokenService,blackService), LoginTokenFilter.class)
                        .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                        .csrf(AbstractHttpConfigurer::disable)
                        .httpBasic(AbstractHttpConfigurer::disable)
                        .formLogin(form -> {
                            form.loginProcessingUrl("/api/user/login");
                        });
                   // .logout(logout -> logout.logoutUrl("/api/user/logout").logoutSuccessHandler(new LogoutTokenSuccessHandler(blackService,tokenService,jwtConfigProperties)).logoutSuccessUrl("/api/user/login"));

        DefaultSecurityFilterChain filterChain = httpSecurity.build();
        // 使用流式API打印过滤器链
        System.out.println("========== Spring Security 过滤器链 ==========");
        filterChain.getFilters().forEach(filter -> {
            System.out.println("过滤器: " + filter.getClass().getName());
            // 可添加自定义过滤器的类型判断
        });
        System.out.println("=============================================");


        return filterChain;
    }
// 这条过滤器 匹配短信验证码登录请求 放行swagger静态资源和发送短信验证码 和短信登录请求
    @Bean
    @Order(2)
    public SecurityFilterChain smsAuthSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // 禁用框架的 X-Frame-Options 响应头，以允许页面在 iframe 中显示
        httpSecurity.headers(headersConfigurer->headersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));

        //配置短信登录接口 多加发送验证码匹配 让发送验证码 下面请求资源放行生效
         httpSecurity.securityMatcher( "/api/user/smsLogin")
                 .authorizeHttpRequests( request->request.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                         .requestMatchers(HttpMethod.POST,"/api/user/smsLogin").permitAll()
                         .anyRequest().authenticated())
                 .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                 .csrf(AbstractHttpConfigurer::disable)
                 .httpBasic(AbstractHttpConfigurer::disable)
                 //.logout(logout -> logout.logoutUrl("/api/user/logout").logoutSuccessHandler(new LogoutTokenSuccessHandler(blackService,tokenService,jwtConfigProperties)).logoutSuccessUrl("/api/user/login"))
                .apply(smsCodeAuthenticationSecurityConfig());
         return httpSecurity.build();
    }

    // 3. 全局 FilterChain (兜底) 其他放行请求资源 存储在这个位置其他请求都要进行验证 访问认证请求 这里进行验证
    // 短信登录存储，没有token也要走这里认证 算了 短信登录成功后也要进行token创建
    @Bean
    @Order(3)
    public SecurityFilterChain globalFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authz -> authz
                        .requestMatchers(HttpMethod.POST,"/api/user/send/phoneCode").permitAll()
                        // 允许swagger访问
                        .requestMatchers("/swagger-ui/**").permitAll()
                        .requestMatchers("/doc.html/**").permitAll()
                        .requestMatchers("/v3/api-docs/**").permitAll()
                        .requestMatchers("/webjars/**").permitAll()
                        .requestMatchers(HttpMethod.POST,"/api/user/generateQrCode").permitAll()
                        .anyRequest().authenticated())
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(new JwtTokenOncePerRequestFilter(userDetailsServiceImpl,jwtConfigProperties,tokenService,blackService), LoginTokenFilter.class)
                .logout(logout -> logout.logoutUrl("/api/user/logout").logoutSuccessHandler(new LogoutTokenSuccessHandler(blackService,tokenService,jwtConfigProperties)).logoutSuccessUrl("/api/user/login"));

        return http.build();
    }



    @Bean
    public UserDetailsService userDetailsService() {
        // 调用 JwtUserDetailService实例执行实际校验
        return username -> userDetailsServiceImpl.loadUserByUsername(username);
    }


    /**
     * 密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 认证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public AuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsServiceImpl);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }




    @Bean
    public SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig() {
        return new SmsCodeAuthenticationSecurityConfig();
    }

}
